const path = require('path')
const fs = require('fs')
const chalk = require('chalk')
const { getFileList, isMatch, successLog, infoLog, errorLog } = require('./util.js')
const jsRenderMd = require('./js/RenderMd.js')
const vueRenderMd = require('./vue/RenderMd.js')
const SidebarV2 = require('./vuepress/SidebarV2.js')
const SidebarV1 = require('./vuepress/SidebarV1.js')
const makeDir = require('make-dir')

class Js2Md {
  /**
   * Creates an instance of Js2Md.
   * @param {*} userDirName 相对的根目录路径
   * @param {*} userConfig 用户的config配置
   * @memberof Js2Md
   */
  constructor(userDirName, userConfig) {
    // 绝对路径列表
    this.srcPathList = []

    // 根目录
    this.userConfigRootPath = userDirName

    // 获取全部的符合条件的文件
    this.filePathList = []

    !userConfig && (userConfig = {})

    // src
    this.srcParams = userConfig['src'] || []
    if (!(userConfig['src'] instanceof Array)) {
      console.error(chalk.bold.red('src参数应该为数组'))
      return
    }

    // exclude 忽略正则
    this.excludeArr = userConfig['exclude'] || []
    if (userConfig['exclude'] && !(userConfig['exclude'] instanceof Array)) {
      console.error(chalk.bold.red('exclude参数应该为数组'))
      return
    }

    // has 包含正则
    this.includeArr = userConfig['include'] || []
    if (userConfig['include'] && !(userConfig['include'] instanceof Array)) {
      console.error(chalk.bold.red('include参数应该为数组'))
      return
    }

    // 生成sidebar的路由
    this.rotuerName = `${userConfig['rotuerName']}` || 'api'

    // 插件md配置
    this.config = userConfig['config']

    // 保存路径
    let dist = userConfig['dist'] || '../docs'
    this.savePath = path.resolve(dist)
    if (!path.isAbsolute(dist)) {
      this.savePath = path.resolve(this.userConfigRootPath, dist)
    }

    // 自定义归类信息
    this.sidebarTree = userConfig['sidebarTree'] || []

    // 默认的api分类名称
    this.defaultSiebarName = userConfig['defaultSiebarName'] || 'API'

    // 输出详情
    this.isOuputInfo = userConfig['info'] !== false

    // vuepress版本
    this.vuepressVersion = userConfig['vuepressVersion'] || 2

    // 源的根目录
    this.isOuputInfo && infoLog('根目录', this.userConfigRootPath)

    // 侧边栏处理程序
    if (this.vuepressVersion >= 2) {
      // v2~
      this.sidebarHandler = new SidebarV2(this.savePath, this.rotuerName, this.sidebarTree, this.defaultSiebarName, this.isOuputInfo)
    } else {
      // v0~v1.x
      this.sidebarHandler = new SidebarV1(this.savePath, this.rotuerName, this.sidebarTree, this.defaultSiebarName, this.isOuputInfo)
    }

    // 主页说明readme.md
    this.readme = userConfig['readme'] || null

    // jsdoc配置项
    this.jsdoc = userConfig['jsdoc'] || null

    // vuedoc配置项
    let vueDoc = {
      autoOutput: {
        title: true,
        props: true,
        slot: true,
        method: true,
        event: true
      }
    }
    this.vueDoc = userConfig['vueDoc'] || vueDoc
    if (this.vueDoc['autoOutput'] === true) {
      this.vueDoc = vueDoc
    } else {
      this.vueDoc['autoOutput'] && (this.vueDoc['autoOutput'] = Object.assign(vueDoc['autoOutput'], this.vueDoc['autoOutput']))
    }
  }

  /**
   * 开始执行
   * @memberof Js2Md
   */
  async start() {
    // infoLog('开始输出...')
    let srcPathList = this.getSrcPathList()
    let srcItemArr = this.getSrcItemList(srcPathList)
    let mdItemArr = this.getMdFileList(srcItemArr)
    let usefulMdItemArr = await this.saveMdFile(mdItemArr)
    this.sidebarHandler && this.sidebarHandler.saveSidebarConfig(usefulMdItemArr)
    this.readmeIndex(usefulMdItemArr)
    successLog('输出完成！')
  }

  /**
   * 生成被保存md文件
   * @param {*} mdItemArr
   * @return {Promise<array>} usefulMdItemArr
   * @memberof Js2Md
   */
  async saveMdFile(mdItemArr) {
    let usefulMdItemArr = []
    for (const mdItem of mdItemArr) {
      // let mdData = jsdoc2md.renderSync({ files: mdItem['fromPath'] })
      let fromPath = mdItem['fromPath']

      let mdData = ''

      let isJs = path.extname(fromPath) == '.js'
      let isVue = path.extname(fromPath) == '.vue'

      if (isJs) {
        // js 导出md
        mdData = await jsRenderMd.render(fromPath, this.jsdoc)
        this.isOuputInfo && infoLog('转换JS', fromPath)
      } else if (isVue) {
        // vue组件导出md
        mdData = await vueRenderMd.render(fromPath, this.vueDoc)
        this.isOuputInfo && infoLog('转换VUE', fromPath)
      }

      // 保存md数据
      if (mdData) {
        let savePath = mdItem['path']
        let dirPath = path.resolve(mdItem['path'], '..')
        // fs.mkdirSync(savePath, { recursive: true })
        makeDir.sync(dirPath)
        this.isOuputInfo && infoLog('输出markdown文件', savePath, '\n')
        fs.writeFileSync(savePath, mdData)
        usefulMdItemArr.push(mdItem)
      } else {
        this.isOuputInfo && errorLog('空数据，已忽略', fromPath, '\n')
      }
    }
    return usefulMdItemArr
  }

  /**
   * 处理输入首页README.md文件
   * @memberof Js2Md
   */
  readmeIndex(usefulMdItemArr) {
    let readmePath = ''
    let readmeData = null
    let readmeName = ''
    let savePath = ''
    if (this.readme) {
      if (this.readme['path']) {
        // readme文件源路径
        readmePath = path.resolve(this.userConfigRootPath, this.readme['path'])
        // readme文件源数据
        readmeData = fs.readFileSync(readmePath)
      }
      // readme文件名
      readmeName = this.readme['outputName'] || 'README.md'
      // readme文件保存路径
      savePath = path.resolve(this.savePath, this.rotuerName, readmeName)
    }

    // 在前面加，#api文件列表
    if (this.readme['isOutputApiList'] !== false) {
      if (!readmeData) {
        readmeData = ''
      }
      // 加数据
      // | 名称      | 链接                                   |
      // | --------- | -------------------------------------- |
      // | RenderMd  | [/api/RenderMd.md](/api/RenderMd.md)   |
      let itemTitle = '\n### API 文档列表\n\n'
      let head = '|名称|链接|\n'
      let headSplit = '| --- | --- |\n'
      let tabelStr = ''
      for (const mdItem of usefulMdItemArr) {
        let urlStr = path.join(mdItem['dir'], `${mdItem['fileName']}`)
        urlStr = urlStr.split(path.sep).join('/')
        let url = `[${mdItem['mdRelative']}](./${urlStr})`
        let tabel = `|${mdItem['name']}|${url}|\n`
        tabelStr += tabel
      }

      if (tabelStr) {
        let title = ''
        if (!readmeData) {
          title = '---\n' + 'title: API文档列表\n' + '---\n'
        }
        let mdStr = title + itemTitle + head + headSplit + tabelStr
        readmeData += mdStr
      }
    }

    if (readmeData) {
      fs.writeFileSync(savePath, readmeData)
      infoLog('写出' + readmeName + '文件', savePath)
    }
  }

  /**
   * 获取输入的源路径的绝对路径
   * @return {*}
   * @memberof Js2Md
   */
  getSrcPathList() {
    let srcPathList = []
    if (this.srcParams instanceof Array) {
      for (const src of this.srcParams) {
        let srcPath = src
        if (!path.isAbsolute(src)) {
          srcPath = path.resolve(this.userConfigRootPath, src)
        }
        srcPathList.push(srcPath)
      }
    } else {
      // 是否为绝对路径
      let srcPath = this.srcParams
      if (!path.isAbsolute(this.srcParams)) {
        srcPath = path.resolve(this.userConfigRootPath, this.srcParams)
      }
      srcPathList.push(srcPath)
    }
    return srcPathList
  }

  /**
   * 获取src列表目录下符合条件的文件列表
   * @param {*} srcPathList 源路径的绝对路径
   * @return {*}
   * @memberof Js2Md
   */
  getSrcItemList(srcPathList) {
    let srcItemArr = []
    // 获取全部的符合条件的文件
    for (const srcPath of srcPathList) {
      let srcItem = {
        // 文件列表
        filePathArr: [],
        // 根目录
        rootPath: ''
      }

      if (fs.statSync(srcPath).isFile()) {
        if (isMatch(this.includeArr, srcPath) && !isMatch(this.excludeArr, srcPath)) {
          srcItem['filePathArr'].push(srcPath)
          srcItem['rootPath'] = srcPath
        }
      } else {
        let arr = getFileList(srcPath, 1, 0, this.includeArr, this.excludeArr)
        srcItem['filePathArr'] = srcItem['filePathArr'].concat(arr)
        srcItem['rootPath'] = srcPath
      }
      srcItemArr.push(srcItem)
    }
    return srcItemArr
  }

  /**
   * 获取需要生成md文件的列表
   * @param {*} srcItemArr
   * @memberof Js2Md
   */
  getMdFileList(srcItemArr) {
    let mdItemArr = []
    for (const srcItem of srcItemArr) {
      let filePathArr = srcItem['filePathArr']
      let rootPath = srcItem['rootPath']
      for (const filePath of filePathArr) {
        let mdItem = {
          name: '',
          path: '',
          ext: '',
          fromExt: '',
          relative: '',
          fromPath: '',
          mdRelative: '',
          sidebarText: '',
          fileName: '',
          dir: ''
        }
        if (!filePath) {
          continue
        }

        let filePathObj = path.parse(filePath)
        // 文件名
        let fileName = filePathObj['name']
        // 文件后缀
        let ext = filePathObj['ext']

        mdItem['name'] = fileName

        mdItem['fromExt'] = ext

        mdItem['ext'] = '.md'

        // md 文件名
        mdItem['fileName'] = `${mdItem['name']}${mdItem['ext']}`

        // js路径
        mdItem['fromPath'] = filePath

        // 相对目录
        mdItem['relative'] = filePath.split(rootPath)[1]
        let relativeObj = path.parse(mdItem['relative'])

        mdItem['dir'] = relativeObj['dir']

        mdItem['mdRelative'] = path.join('/', this.rotuerName, mdItem['dir'], `${mdItem['fileName']}`)

        // 导航栏左边的名称
        mdItem['sidebarText'] = mdItem['dir'].split(path.sep)[0]

        // md文件路径
        mdItem['path'] = path.join(this.savePath, mdItem['mdRelative'])

        mdItemArr.push(mdItem)
      }
    }
    return mdItemArr
  }
}

module.exports = Js2Md
